package algorithms;

import infoPackage.*;

import java.util.ArrayList;

//http://www.softtechdesign.com/GA/GA-Javadocs/com/softtechdesign/ga/GAString.html
public class Chromosome {

    float ps = -1;  // Selection Probability
    float pcr = -1; // Crossover Probability
    float pm = -1;  // Mutation Probability
    /** Maximum of dams to construct in the same river */
    public int maxDam = 3;
    /** Minimum of distance, in km, necessary between 2 dams*/
    public float minDistance = 3;
    /** Helps in selection task */
    private boolean selected = false;
    private int crossover = -1;				// Point to cross the chromosomes
    private int mutation = -1;				// Value to mutate
    private float value = Float.MIN_VALUE;	// Adaptation Function Value
    public ArrayList<Dam> chromosome = new ArrayList<Dam>();

    public Chromosome() {
    }

    public Chromosome(int size, int maxDam, float minDistance) {
        this.maxDam = maxDam;
        this.minDistance = minDistance;
        for (int i = 0; i < size; i++) {
            boolean construct = Dam.randomConstruct();
            float capacity = Dam.randomCapacity();
            float volume = Dam.randomVolume();
            int tecnology = Dam.randomTecnology();
            int natureImpact = Dam.randomNatureImpact();
            ArrayList<Material> mat = Dam.randomMaterials();
            chromosome.add(new Dam(construct, capacity, volume, tecnology, natureImpact, mat));
        }
    }

    public void adaptationFunction(ArrayList<Section> sections) {
        int totalDam = 0; 	// total number of dams to be constructed
        int river = -1; 	// id of the river
        for (int i = 0; i < chromosome.size(); i++) {
            Dam dam = chromosome.get(i);
            Section section = sections.get(i);

            if (dam.isConstruct()) {
                //No connection to power
                if (!section.power) {
                    this.value = 0;
                    break;
                } else {

                    if (river == -1 || river == section.river) {
                        totalDam++;
                        // Two dams in the same river must be at least a minDistance between them.
                        // requires an ordered file. the sections must follow an increasing order of offset
                        if (i > 0 && section.offset < (sections.get(i - 1).offset + minDistance)) {
                            this.value = 0;
                            break;
                        }
                    } else {
                        // No more than maxDam dams shall be constructed in the same river
                        if (totalDam == 0 || totalDam > maxDam) {
                            this.value = 0;
                            break;
                        }
                        totalDam = 1;
                    }
                    river = section.river;
                    //the damn must be a minimum volume, defined in the file, for each section
                    if (dam.getVolume() < section.minVolume) {
                        this.value = 0;
                        break;
                    }

                    //Price of the components
                    float component_price = 0;
                    for (int j = 0; j < dam.materials.size(); j++) {
                        Material mat = dam.materials.get(j);
                        component_price += dam.getVolume() * mat.getPercentage() * mat.getPrice();
                    }

                    // need for an extra structure - fish stairway
                    if (section.struct) {
                        component_price += 1000;
                    }

                    //component_price += natureValue(dam.getNatureImpact());
                    component_price += tecnologyValue(dam.getTecnology());

                    //Price of the field
                    float field_price = section.cost * section.size;

                    float field_quality = section.h * 10000 / section.narrow + accessValue(section.access)
                            - 40 * floatValue(section.flora) - 60 * floatValue(section.fauna)
                            + section.people + natureValue(dam.getNatureImpact());

                    float efficiency = dam.getCapacity() * dam.getTecnology() / (section.flow * section.h) * 10000000;
                    if (efficiency < 0.5) {
                        this.value = 0;
                        break;
                    }

                    System.out.println("           Efficiency:" + efficiency
                            + "\n           Comp price:" + component_price
                            + "\n           Field quality: " + field_quality
                            + "\n           Price: " + field_price
                            + "\n           fq/fp: " + field_quality / field_price + "\n");

                    this.value += efficiency / (component_price + (field_quality / field_price));
                }
            } else {
                this.value += 0;
            }

        }
        System.out.println("            Total Value: " + this.value);
    }

    public int floatValue(boolean b) {
        return b ? 1 : 0;
    }

    public int natureValue(int n) {
        switch (n) {
            case 0:
                return 200;
            case 1:
                return 100;
            case 2:
                return 0;
            case 3:
                return -100;
            case 4:
                return -200;
            default:
                return 10;
        }
    }

    public int accessValue(int n) {
        switch (n) {
            case 0: //muito mau
                return -300;
            case 1: //mau
                return -150;
            case 2:	//normal
                return 0;
            case 3://bom
                return 150;
            case 4://muito bom
                return 300;
            default:
                return 1;
        }
    }

    public int tecnologyValue(int n) {
        switch (n) {
            case 1: //mau
                return 1000;
            case 2:	//normal
                return 2000;
            case 3://bom
                return 3000;
            case 4://muito bom
                return 10000;
            default:
                return 50;
        }
    }

    public void printChromosome() {
        for (int i = 0; i < chromosome.size(); i++) {
            Dam d = chromosome.get(i);
            System.out.print(" Dam " + i
                    + " | Construct: " + d.isConstruct()
                    + " | Capacity: " + d.getCapacity()
                    + " | Volume: " + d.getVolume()
                    + " | Nature: " + d.getNatureImpact()
                    + " | Tecnology: " + d.getTecnology());
            System.out.println(" | Materials: ");
            for (int j = 0; j < d.getMaterials().size(); j++) {
                d.getMaterials().get(j).printMaterial();
            }

        }
    }

    /**
     * @param value the value to set
     */
    public void setValue(float value) {
        this.value = value;
    }

    /**
     * @return the value
     */
    public float getValue() {
        return value;
    }

    /**
     * @param selected the selected to set
     */
    public void setSelected(boolean selected) {
        this.selected = selected;
    }

    /**
     * @return the selected
     */
    public boolean isSelected() {
        return selected;
    }

    /**
     * @param crossover the crossover to set
     */
    public void setCrossover(int crossover) {
        this.crossover = crossover;
    }

    /**
     * @return the crossover
     */
    public int getCrossover() {
        return crossover;
    }

    /**
     * @param mutation the mutation to set
     */
    public void setMutation(int mutation) {
        this.mutation = mutation;
    }

    /**
     * @return the mutation
     */
    public int getMutation() {
        return mutation;
    }
}
